package com.juc.rwlock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * @Author : Zan
 * @Create : 2024/5/15 19:42
 * @ClassName : StampedLockDemo
 * @Description : TODO 请用一句话描述该类的功能
 */
public class StampedLockDemo {

    static int number = 37;

    static StampedLock stampedLock = new StampedLock();

    public void write() {
        long stamp = stampedLock.writeLock(); // 返回戳记（流水号）
        System.out.println(Thread.currentThread().getName() + "\t 写线程准备修改");
        try {
            number = number + 13;
        } finally {
            stampedLock.unlockWrite(stamp);
        }
        System.out.println(Thread.currentThread().getName() + "\t 写线程结束修改");
    }

    // 悲观读，读没有完成时候写锁无法获取锁
    public void read() {
        long stamp = stampedLock.readLock();
        System.out.println(Thread.currentThread().getName() + "\t come in readlock code lock, 4 seconds continue...");
        for (int i = 0; i < 4; i++) {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "\t 正在读取中....");
        }
        try {
            int result = number;
            System.out.println(Thread.currentThread().getName() + "\t 获得成员变量值result：" + result);
            System.out.println("写线程没有修改成功，读锁时候写锁无法介入，传统的读写互斥");
        } finally {
            stampedLock.unlockRead(stamp);
        }
    }

    // 乐观读，读的过程中也允许获取写锁介入
    public void tryOptimisticRead() {
        long stamp = stampedLock.tryOptimisticRead();
        int result = number;
        // 故意间隔4s，很乐观认为读取中没有其他线程修改过number值，具体靠判断
        System.out.println("4s前stampedLock.validate方法值(true无修改，false有人动过)" + "\t" + stampedLock.validate(stamp));
        for (int i = 0; i < 4; i++) {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "\t正在读取... " + i +
                    " 秒后stampedLock.validate方法值(true无修改，false有人动过)\t" + stampedLock.validate(stamp));
        }
        if (!stampedLock.validate(stamp)) {
            // 有修改
            System.out.println("有人修改过-----有写操作");
            stamp = stampedLock.readLock();
            try {
                System.out.println("从乐观读 升级为 悲观读");
                result = number;
                System.out.println("重新悲观读后result：" + result);
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }
        // 没有人修改，乐观成功
        System.out.println(Thread.currentThread().getName() + "\t finally value：" + result);
    }

    public static void main(String[] args) {
        StampedLockDemo resource = new StampedLockDemo();

        // 新版本
        new Thread(() -> {
            resource.tryOptimisticRead();
        }, "optimisticReadThread").start();

        // 暂停2s，读过程可以写介入
        /*try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 进行写线程
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t --- come in");
            resource.write();
        }, "writeThread").start();

        // 传统版
        /*new Thread(() -> {
            resource.read();
        }, "readThread").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t --- come in");
            resource.write();
        }, "writeThread").start();

        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "\t" + StampedLockDemo.number);*/
    }
}
